#include "common.h"
#include "Camera.h"
#include "Mesh.h"
#include "Scene.h"
#include "Texture.h"
#include "TreeSpec.h"

using namespace cg;
using namespace std;

///////////////////////////////////////////////////////////////////////////////
// functions for glut registration
///////////////////////////////////////////////////////////////////////////////

Camera				camera;
Scene				scene;
TextMan				textMan( "data" );

void InitRC()
{	
	glutSetCursor( GLUT_CURSOR_NONE ); 
	//
	scene.Init();	
	float v = 1.0f;
	scene.AmbientLight( Vec4( v, v, v, 1.0f ) );
	int id = scene.GetDisabledLightId();
	Light *l = scene.GetLight( id );
	l->SetPosition( Vec4::Y_AXIS * 100 );
	l->Enable();	
	//
	glEnable( GL_DEPTH_TEST );
	glEnable( GL_CULL_FACE );
	glCullFace( GL_BACK );
	glClearColor( Color::DEEP_SKY_BLUE.x, Color::DEEP_SKY_BLUE.y, Color::DEEP_SKY_BLUE.z, 1.0f );		
	//			
	TreeSpecMan treeSpecMan;
	Mutator mutator;
	mutator.scale = 1.0f;	
	Entity *entity = treeSpecMan.GenerateTree( treeSpecMan.tree3, textMan, mutator );					
	scene.AddEntity( entity );
	{
		
		Quad *quad = new Quad( Vec3( -10.0f, 0.0f, 10.0f ),
							   Vec3( 20.0f, 0.0f, 0.0f ),
							   Vec3( 0.0f, 0.0f, -20.0f ) );
		Entity *lawn = new Entity( quad );			
		lawn->material.SetAmbientAndDiffuseColor( Color::DARK_GREEN );
		scene.AddEntity( lawn );
	}
}
int width = 800;
int height = 600;

void ResizeWindow( int w, int h )
{
	if( h == 0 )
		h = 1;
	glViewport( 0, 0, w, h );

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	float W = 0.01f;
	float H = W * h / w;	
	glFrustum( -W, W, -H, H, W, 300.0f );	
	glMatrixMode( GL_MODELVIEW );	

	width = w;
	height = h;

	glutWarpPointer( width / 2 , height / 2 );
}

void GameLoop()
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	{
		glLoadIdentity();				
		camera.Work();
		scene.Render();
	}
	glutSwapBuffers();
	glutPostRedisplay();	
}

float step = 0.1f; 

void Keys( unsigned char key, int, int )
{
	if( key == 27 )
		exit( 1 );

	if( key == 'w' )
		camera.MoveForward( step );
	
	if( key == 's' )
		camera.MoveForward( -step );

	if( key == 'a' )
		camera.MoveRight( -step );

	if( key == 'd' )
		camera.MoveRight( step );

	glutPostRedisplay();	
}

void SpecialKeys( int key, int, int )
{
	if( key == GLUT_KEY_UP )
		camera.MoveForward( step );
	
	if( key == GLUT_KEY_DOWN )
		camera.MoveForward( -step );

	if( key == GLUT_KEY_LEFT )
		camera.MoveRight( -step );

	if( key == GLUT_KEY_RIGHT )
		camera.MoveRight( step );

	glutPostRedisplay();	
}

void MouseMove( int x, int y )
{	
	int sx = width / 2;
	int sy = height / 2;
	if ( x != sx || y != sy ) {
		float maxDist = 50;
		//
		Vec2 v( static_cast<float>( x - sx ), static_cast<float>( y - sy ) );	
		if ( v.Len() > maxDist) {
			v.Normalize();
			v *= maxDist;
		}	
		camera.Yaw( TO_RAD( -v.x ) );
		camera.Pitch( TO_RAD( -v.y ) );
		glutWarpPointer( sx, sy );
	}
}

///////////////////////////////////////////////////////////////////////////////
// main
///////////////////////////////////////////////////////////////////////////////

int main( int argc, char *argv[] )
{		
	glutInit( &argc, argv );
	glutInitWindowSize( width , height );
	glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE );	
	//
	glutCreateWindow( "Space colonization" );	
	glutReshapeFunc( ResizeWindow );
	glutSpecialFunc( SpecialKeys );
	glutKeyboardFunc( Keys );
	glutPassiveMotionFunc ( MouseMove );
	//
	Random::Seed( static_cast<unsigned int>( time(NULL) ) );
	//
	InitRC();
	//
	glutDisplayFunc( GameLoop );
	glutMainLoop();
}